package com.miao.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.miao.FeignClient.MediaServiceClient;
import com.miao.base.exception.XueChengPlusException;
import com.miao.config.MultipartSupportConfig;
import com.miao.dto.CourseBaseInfoDto;
import com.miao.dto.CoursePreviewDto;
import com.miao.dto.TeachPlanDto;
import com.miao.mapper.CoursePublishMapper;
import com.miao.pojo.*;
import com.miao.service.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class CoursePublishServiceImpl extends ServiceImpl<CoursePublishMapper, CoursePublish> implements ICoursePublishService {

    @Autowired
    private ICourseBaseService courseBaseService;
    @Autowired
    private ITeachplanService teachplanService;
    @Autowired
    private ICourseMarketService courseMarketService;
    @Autowired
    private ICourseTeacherService courseTeacherService;
    @Autowired
    private ICoursePublishPreService coursePublishPreService;
    @Autowired
    private CoursePublishMapper coursePublishMapper;
    @Autowired
    private MqMessageService mqMessageService;
    @Autowired
    private MediaServiceClient mediaServiceClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    //查询预览信息
    @Override
    public CoursePreviewDto getCoursePreview(Long courseId) {
        CoursePreviewDto coursePreviewDto = new CoursePreviewDto();
        CourseBaseInfoDto courseInfoDto = courseBaseService.getCourseInfoDto(courseId);
        coursePreviewDto.setCourseBase(courseInfoDto);
        List<TeachPlanDto> teachPlanDtos = teachplanService.selectTeachById(courseId);
        coursePreviewDto.setTeachplans(teachPlanDtos);
        return coursePreviewDto;
    }

    //提交课程审核
    @Transactional
    @Override
    public void commitAuditCourse(Long courseId, Long comId) {
        CourseBaseInfoDto courseInfoDto = courseBaseService.getCourseInfoDto(courseId);
        if(courseInfoDto == null){
            XueChengPlusException.cast("该课程为空！");
        }
        //公司核验
        if(!comId.equals(courseInfoDto.getCompanyId())){
            XueChengPlusException.cast("本公司只可以提交本公司的课程！！");
        }
        //获取审核状态
        String auditStatus = courseInfoDto.getAuditStatus();
        if("202003".equals(auditStatus)){
            XueChengPlusException.cast("课程已经提交，等待审核！！");
        }
        //查询基本信息，营销信息，计划信息到预发布表
        List<TeachPlanDto> teachPlanDtos = teachplanService.selectTeachById(courseId);
        if(teachPlanDtos == null || teachPlanDtos.size() == 0){
            XueChengPlusException.cast("无课程计划！！！");
        }
        //基本信息
        CoursePublishPre coursePublishPre = new CoursePublishPre();
        BeanUtils.copyProperties(courseInfoDto, coursePublishPre);
        //营销信息
        CourseMarket courseMarket = courseMarketService.getById(courseId);
        String jsonOfMarket = JSON.toJSONString(courseMarket);
        coursePublishPre.setMarket(jsonOfMarket);
        //计划信息
        String jsonOfPlan = JSON.toJSONString(teachPlanDtos);
        coursePublishPre.setTeachplan(jsonOfPlan);
        //师资信息
        LambdaQueryWrapper<CourseTeacher> courseTeacherLambdaQueryWrapper = new LambdaQueryWrapper<>();
        courseTeacherLambdaQueryWrapper.eq(CourseTeacher::getCourseId, courseId);
        List<CourseTeacher> list = courseTeacherService.list(courseTeacherLambdaQueryWrapper);
        String jsonOfTeachers = JSON.toJSONString(list);
        coursePublishPre.setTeachers(jsonOfTeachers);
        //公司信息
        coursePublishPre.setCompanyId(comId);
        //状态
        coursePublishPre.setStatus("202003");
        //提交时间
        coursePublishPre.setCreateDate(LocalDateTime.now());
        //更新或者插入
        coursePublishPreService.saveOrUpdate(coursePublishPre);


        //更新课程基本信息表的状态为已提交
        CourseBase courseBase = courseBaseService.getById(courseId);
        courseBase.setAuditStatus("202003");
        courseBaseService.updateById(courseBase);
    }

    //发布课程
    @Transactional
    @Override
    public void publishCourse(Long courseId, Long comId) {
        CoursePublishPre coursePublishPre = coursePublishPreService.getById(courseId);
        if(!comId.equals(coursePublishPre.getCompanyId())){
            XueChengPlusException.cast("本公司只能提交本公司课程！");
        }
        String status = coursePublishPre.getStatus();
        if(!"202004".equals(status)){
            XueChengPlusException.cast("未审核课程无法提交！");
        }
        //将预发布表拷贝到发布表
        CoursePublish coursePublish = new CoursePublish();
        BeanUtils.copyProperties(coursePublishPre, coursePublish);
        CoursePublish _coursePublish = coursePublishMapper.selectById(courseId);
        if(_coursePublish == null){//无则插入，有则修改
            coursePublishMapper.insert(coursePublish);
        }else{
            coursePublishMapper.updateById(coursePublish);
        }
        //向消息表中写入数据------------------------------------
        mqMessageService.addMessage("course_publish", String.valueOf(courseId), null, null);
        //删除预发布表数据----------------------------------------
        coursePublishPreService.removeById(courseId);
    }

    //上传生成的html页面
    @Override
    public void uploadHtml(Long courseId, File file) throws Exception {
        //将文件转为Multipart格式（方便远程调用）
        MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(file);
        //远程调用
        String fuck = mediaServiceClient.fuck(multipartFile, "course/" + courseId + ".html");

        if(fuck == null){
            XueChengPlusException.cast("上传静态页面到minio出错！");
        }

    }
    //生成html页面,使用freemarker
    @Override
    public File geneteHtml(Long courseId) throws IOException, TemplateException {
        Configuration configuration = new Configuration(Configuration.getVersion());
        //拿到模板
        String path = this.getClass().getResource("/").getPath();
        configuration.setDirectoryForTemplateLoading(new File(path + "/templates/"));
        configuration.setDefaultEncoding("utf-8");
        Template template = configuration.getTemplate("course_template2.ftl");
        //准备数据
        CoursePreviewDto coursePreview = getCoursePreview(courseId);
        Map<String, Object> map = new HashMap<>();
        map.put("model", coursePreview);
        //返回html字符串
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
        //生成文件
        InputStream stream = IOUtils.toInputStream(html, "utf-8");
        File file = new File("D:\\java_web_html\\" + courseId + ".html");
        IOUtils.copy(stream, new FileOutputStream(file));
        return file;
    }
    //从redis中获取课程信息，在用户观看时减小数据库压力
    @Override
    public CoursePublish getCoursePublishCache(Long courseId) {
        //缓存查询
        Object o = redisTemplate.opsForValue().get("course:" + courseId);
        if(o != null){//有
            String json = o.toString();
            if(json.equals("null")){
                return null;//防止缓存穿透
            }
            return JSON.parseObject(json, CoursePublish.class);
        }else{//从数据库查询
            //每门课程设置一个锁
            RLock lock = redissonClient.getLock("coursequerylock:"+courseId);
            //获取锁
            lock.lock();
            try {//防止缓存击穿
                //再次查一下，防止已经加进去了
                o = redisTemplate.opsForValue().get("course:" + courseId);
                if (o != null) {//有
                    String json = o.toString();
                    if (json.equals("null")) {
                        return null;//防止缓存穿透
                    }
                    return JSON.parseObject(json, CoursePublish.class);
                }

                CoursePublish coursePublish = getById(courseId);
                Object json = JSON.toJSON(coursePublish);
                //最多缓存30s
                //防止：有个id对应null，有一天真的插入这个id的课程，还是null，不会变，所以设置缓存时间
                redisTemplate.opsForValue().set("course:" + courseId, json, 30, TimeUnit.SECONDS);
                return coursePublish;
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
}
